﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
using ECube.Web.CMS.Models;
namespace ECube.Web.CMS.Helpers
{
    /// <summary>
    /// HtmlHelper extension methods
    /// </summary>
    public static class MenuHelper
    {
        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, string selectedMenuItemCssClass)
        {
            return Menu(helper, true, false, selectedMenuItemCssClass, null);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <param name="menuHtmlAttributes">Html attributes for the outer unordered list</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, string selectedMenuItemCssClass, object menuHtmlAttributes)
        {
            return Menu(helper, true, false, selectedMenuItemCssClass, menuHtmlAttributes);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="showStartingNode">Show starting node?</param>
        /// <param name="startFromCurrentNode">Start from current node?</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <param name="menuHtmlAttributes">Html attributes for the outer unordered list</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, bool showStartingNode, bool startFromCurrentNode, string selectedMenuItemCssClass, object menuHtmlAttributes)
        {
            return Menu(helper, SiteMap.Provider.Name, showStartingNode, startFromCurrentNode, selectedMenuItemCssClass, 1, menuHtmlAttributes);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="providerName">Provider name (can be used to specify SiteMap datasource)</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, string providerName, string selectedMenuItemCssClass)
        {
            return Menu(helper, providerName, true, false, selectedMenuItemCssClass, 1, null);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="providerName">Provider name (can be used to specify SiteMap datasource)</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <param name="menuHtmlAttributes">Html attributes for the outer unordered list</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, string providerName, string selectedMenuItemCssClass, int maxLevels, object menuHtmlAttributes)
        {
            return Menu(helper, providerName, true, false, selectedMenuItemCssClass, maxLevels, menuHtmlAttributes);
        }



        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="providerName">Provider name (can be used to specify SiteMap datasource)</param>
        /// <param name="showStartingNode">Show starting node?</param>
        /// <param name="startFromCurrentNode">Start from current node?</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <param name="menuHtmlAttributes">Html attributes for the outer unordered list</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, string providerName, bool showStartingNode, bool startFromCurrentNode, string selectedMenuItemCssClass, int maxLevels, object menuHtmlAttributes)
        {
            return Menu(helper, providerName, showStartingNode, 0, startFromCurrentNode, selectedMenuItemCssClass, maxLevels, menuHtmlAttributes, -1);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="startingNodeLevel">Starting node level</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, int startingNodeLevel, string selectedMenuItemCssClass)
        {
            return Menu(helper, SiteMap.Provider.Name, false, startingNodeLevel, true, selectedMenuItemCssClass, 1, null, -1);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="startingNodeLevel">Starting node level</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <param name="menuHtmlAttributes">Html attributes for the outer unordered list</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, int startingNodeLevel, string selectedMenuItemCssClass, object menuHtmlAttributes)
        {
            return Menu(helper, SiteMap.Provider.Name, false, startingNodeLevel, true, selectedMenuItemCssClass, 1, null, -1);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="providerName">Provider name (can be used to specify SiteMap datasource)</param>
        /// <param name="startingNodeLevel">Starting node level</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, string providerName, int startingNodeLevel, string selectedMenuItemCssClass)
        {
            return Menu(helper, providerName, false, startingNodeLevel, true, selectedMenuItemCssClass, 1, null, -1);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="providerName">Provider name (can be used to specify SiteMap datasource)</param>
        /// <param name="startingNodeLevel">Starting node level</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <param name="menuHtmlAttributes">Html attributes for the outer unordered list</param>
        /// <returns>Html markup</returns>
        public static string Menu(this HtmlHelper helper, string providerName, int startingNodeLevel, string selectedMenuItemCssClass, object menuHtmlAttributes)
        {
            return Menu(helper, providerName, false, startingNodeLevel, true, selectedMenuItemCssClass, 1, menuHtmlAttributes, -1);
        }

        /// <summary>
        /// Build a pinned menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="providerName">Provider name (can be used to specify SiteMap datasource)</param>
        /// <param name="pinnedLevel">Pinned level</param>
        /// <param name="menuHtmlAttributes">Html attributes for the outer unordered list</param>
        /// <returns>Html markup</returns>
        public static string MenuPinned(this HtmlHelper helper, string providerName, int pinnedLevel, object menuHtmlAttributes)
        {
            return Menu(helper, providerName, false, 0, true, string.Empty, -1, menuHtmlAttributes, pinnedLevel);
        }

        /// <summary>
        /// Build a menu, based on the MvcSiteMap
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="providerName">Provider name (can be used to specify SiteMap datasource)</param>
        /// <param name="showStartingNode">Show starting node?</param>
        /// <param name="startFromCurrentNode">Start from current node?</param>
        /// <param name="startingNodeLevel">Starting node level</param>
        /// <param name="selectedMenuItemCssClass">Selected menu item CSS class</param>
        /// <param name="menuHtmlAttributes">Html attributes for the outer unordered list</param>
        /// <returns>Html markup</returns>  
        public static string Menu(this HtmlHelper helper, string providerName, bool showStartingNode, int startingNodeLevel, bool startFromCurrentNode, string selectedMenuItemCssClass, int maxLevels, object menuHtmlAttributes, int pinnedNodeLevel)
        {
            SiteMapProvider selectedSiteMapProvider = SiteMap.Providers[providerName];
            // Check for provider
            if (selectedSiteMapProvider == null)
            {
                throw new UnknownSiteMapProviderException(string.Format("Unknown SiteMap provider: {0}", providerName));
            }

            // Helpers
            UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

            // Html attributes
            IDictionary<string, object> attributes = new RouteValueDictionary(menuHtmlAttributes);

            // Determine root node
            SiteMapNode relativeRootNode = null;
            if (startFromCurrentNode)
            {
                relativeRootNode = selectedSiteMapProvider.CurrentNode;
                if (relativeRootNode == null)
                    relativeRootNode = selectedSiteMapProvider.RootNode;
                else if (pinnedNodeLevel>0)
                {
                    while (GetNodeLevel(relativeRootNode)>pinnedNodeLevel)
                    {
                        relativeRootNode = relativeRootNode.ParentNode;
                    }
                }
            }
            else
            {
                relativeRootNode = selectedSiteMapProvider.RootNode;
            }


            // Node not defined?
            if (relativeRootNode == null)
            {
                // String builder
                StringBuilder sb1 = new StringBuilder();
                CreateUnorderedListTag(helper, selectedMenuItemCssClass, selectedSiteMapProvider, urlHelper, attributes, new SiteMapNodeCollection(), sb1, maxLevels);
                return sb1.ToString();
            }


            // Fetch nodes to render
            SiteMapNodeCollection nodesToRender = new SiteMapNodeCollection();
            if (showStartingNode)
            {
                nodesToRender.Add(relativeRootNode);
            }
            if (relativeRootNode.HasChildNodes)
            {
                // Use startingNodeLevel?
                if (startingNodeLevel == 0)
                {
                    // no...
                    nodesToRender.AddRange(relativeRootNode.ChildNodes);
                }
                else
                {
                    // yes: determine absolute level of relativeRootNode
                    int startingNodeAbsoluteLevel = GetNodeLevel(relativeRootNode);
                    if (startingNodeAbsoluteLevel + 1 == startingNodeLevel)
                    {
                        nodesToRender.AddRange(relativeRootNode.ChildNodes);
                    }
                }
            }


            // String builder
            StringBuilder sb = new StringBuilder();
            CreateUnorderedListTag(helper, selectedMenuItemCssClass, selectedSiteMapProvider, urlHelper, attributes, nodesToRender, sb, maxLevels);
            return sb.ToString();
        }

        /// <summary>
        /// Creates a UL tag
        /// </summary>
        /// <param name="helper">HtmlHelper instance</param>
        /// <param name="selectedMenuItemCssClass">Selected MenuItem Css Class</param>
        /// <param name="selectedSiteMapProvider">Selected SiteMapProvider</param>
        /// <param name="urlHelper">UrlHelper instance</param>
        /// <param name="attributes">Attributes</param>
        /// <param name="nodesToRender">List of nodes to render</param>
        /// <param name="sb">StringBuilder to work with</param>
        /// <param name="maxLevels">Maximal number of levels</param>
        private static void CreateUnorderedListTag(HtmlHelper helper, string selectedMenuItemCssClass, SiteMapProvider selectedSiteMapProvider, UrlHelper urlHelper, IDictionary<string, object> attributes, SiteMapNodeCollection nodesToRender, StringBuilder sb, int maxLevels) 
        {
            // Create unordered list tag
            sb.Append("<ul");
            if (attributes != null)
            {
                foreach (var attribute in attributes)
                {
                    sb.Append(string.Format(" {0}=\"{1}\"", attribute.Key, attribute.Value));
                }
            }
            sb.AppendLine(">");

            // Render each node
            foreach (SiteMapNode node in nodesToRender)
            {
                MvcSiteMapNode mvcNode = node as MvcSiteMapNode;
                bool nodeVisible = true;

                // Node authorized?
                if (!node.IsAccessibleToUser(HttpContext.Current))
                    nodeVisible = false;

                // Node visible?
                if (mvcNode != null && mvcNode.Visibility != MvcSiteMapNodeVisibility.Full)
                    nodeVisible = false;

                // Render node
                if (nodeVisible)
                {
                    if (selectedSiteMapProvider.CurrentNode != null && (selectedSiteMapProvider.CurrentNode.Equals(node) || selectedSiteMapProvider.CurrentNode.IsDescendantOf(node)))
                    {
                        sb.Append("<li");
                        if (!string.IsNullOrEmpty(selectedMenuItemCssClass))
                        {
                            sb.Append(string.Format(" class=\"{0}\"", selectedMenuItemCssClass));
                        }
                        sb.Append(">");
                    }
                    else
                    {
                        sb.Append("<li>");
                    }

                    string url = string.Empty;
                    if (!string.IsNullOrEmpty(node.Url))
                        url = urlHelper.Content(node.Url);

                    string imageMarkup = string.Empty;
                    if (mvcNode != null && !string.IsNullOrEmpty(mvcNode.ImageUrl))
                    {
                        string imageUrl = urlHelper.Content(mvcNode.ImageUrl);
                        imageMarkup = string.Format("<img src=\"{0}\" class=\"icon\" alt=\"{1}\" />", imageUrl, helper.Encode(node.Title));
                    }

                    sb.AppendFormat("<a href=\"{0}\">{1}{2}</a>", url, imageMarkup, helper.Encode(node.Title));
                    if (maxLevels != 1 && node.HasChildNodes)
                        CreateUnorderedListTag(helper, selectedMenuItemCssClass, selectedSiteMapProvider, urlHelper, null, node.ChildNodes, sb, maxLevels--);

                    sb.AppendLine("</li>");
                }
            }

            // Close unordered list tag
            sb.AppendLine("</ul>");
        }

        /// <summary>
        /// Get SiteMapNode level
        /// </summary>
        /// <param name="node">SiteMapNode to determine level for</param>
        /// <returns>SiteMapNode level</returns>
        public static int GetNodeLevel(SiteMapNode node)
        {
            int level = 0;
            while (node.ParentNode != null)
            {
                level++;
                node = node.ParentNode;
            }
            return level;
        }


        ////public static void RenderMainMenu(this HtmlHelper helper)
        ////{
        ////    RenderMenu(helper, "MainMenu");
        ////}
        //public static void RenderMenu(this HtmlHelper helper, String viewname)
        //{

        //    helper.RenderPartial(viewname, SiteNavigatorService.Current.RootNode);
        //    return;







        //    SiteMapProvider selectedSiteMapProvider = SiteMap.Provider;
        //    // Check for provider
        //    if (selectedSiteMapProvider == null)
        //    {
        //        throw new UnknownSiteMapProviderException("Unknown SiteMap provider");
        //    }

        //    // Helpers
        //   // UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

        //    // Html attributes
        //   // IDictionary<string, object> attributes = new RouteValueDictionary(menuHtmlAttributes);

        //    // Determine root node
        //    SiteMapNode relativeRootNode = null;
        //    //if (startFromCurrentNode)
        //    //{
        //    //    relativeRootNode = selectedSiteMapProvider.CurrentNode;
        //    //    if (relativeRootNode == null)
        //    //        relativeRootNode = selectedSiteMapProvider.RootNode;
        //    //    else if (pinnedNodeLevel > 0)
        //    //    {
        //    //        while (GetNodeLevel(relativeRootNode) > pinnedNodeLevel)
        //    //        {
        //    //            relativeRootNode = relativeRootNode.ParentNode;
        //    //        }
        //    //    }
        //    //}
        //   // else
        //   // {
        //        relativeRootNode = selectedSiteMapProvider.RootNode;
        //   // }


            

            

        

        //    helper.RenderPartial(viewname, relativeRootNode);
        //}

        //public static String SideMenu(this HtmlHelper helper)
        //{
        //    return SideMenu(helper, SiteMap.Provider.Name,true,0,true,"selected",2,null,2);
        //}
        //public static String SideMenu(this HtmlHelper helper, string providerName, bool showStartingNode, int startingNodeLevel, bool startFromCurrentNode, string selectedMenuItemCssClass, int maxLevels, object menuHtmlAttributes, int pinnedNodeLevel)
        //{
        //    helper.RenderPartial("SideMenu");
        //    return "";
        //    SiteMapProvider selectedSiteMapProvider = SiteMap.Providers[providerName];
        //    // Check for provider
        //    if (selectedSiteMapProvider == null)
        //    {
        //        throw new UnknownSiteMapProviderException(string.Format("Unknown SiteMap provider: {0}", providerName));
        //    }

        //    // Helpers
        //    UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

        //    // Html attributes
        //    IDictionary<string, object> attributes = new RouteValueDictionary(menuHtmlAttributes);

        //    // Determine root node
        //    SiteMapNode relativeRootNode = selectedSiteMapProvider.CurrentNode;
           
        //    // Node not defined?
        //    if (relativeRootNode == null)
        //    {
        //        // String builder
        //        StringBuilder sb1 = new StringBuilder();
        //        CreateUnorderedListTag(helper, selectedMenuItemCssClass, selectedSiteMapProvider, urlHelper, attributes, new SiteMapNodeCollection(), sb1, maxLevels);
        //        return sb1.ToString();
        //    }

            
        //    // Fetch nodes to render
        //    SiteMapNodeCollection nodesToRender = new SiteMapNodeCollection();
        //    if (showStartingNode)
        //    {
        //        nodesToRender.Add(relativeRootNode);
        //    }
        //    if (relativeRootNode.HasChildNodes)
        //    {
        //        // Use startingNodeLevel?
        //        if (startingNodeLevel == 0)
        //        {
        //            // no...
        //            nodesToRender.AddRange(relativeRootNode.ChildNodes);
        //        }
        //        else
        //        {
        //            // yes: determine absolute level of relativeRootNode
        //            int startingNodeAbsoluteLevel = GetNodeLevel(relativeRootNode);
        //            if (startingNodeAbsoluteLevel + 1 == startingNodeLevel)
        //            {
        //                nodesToRender.AddRange(relativeRootNode.ChildNodes);
        //            }
        //        }
        //    }


        //    // String builder
        //    StringBuilder sb = new StringBuilder();
        //    CreateUnorderedListTag(helper, selectedMenuItemCssClass, selectedSiteMapProvider, urlHelper, attributes, nodesToRender, sb, maxLevels);
        //    return sb.ToString();
        //}
    }
}